package com.example.threadcode.higherLeverThree.PC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 主方法实例化Shared(),Producer,Consumer类.shared作为参数传递进Producer和Consumer实例,然后P C线程启动.
 * Shared实例必须对PC线程是可见的(尤其是那些运行在不同CPU核上的线程.).在每一个PC中,通过声明S为final来完成那些任务
 * 对Shared的属性域声明为volatile,但是volatile建议对该字段进行额外的写操作并且在初始化之后S不应该被改变
 * 检查Shared的构造器.注意到,它创建一个锁通过new ReentrantLock();创建一个与這个锁关联的条件对象通过lock.newCondition()
 * 通过Lock的getLock()方法,使得這个锁对于PC线程可用.

 *
 */
public class PC
{
   public static void main(String[] args)
   {
      Shared s = new Shared();
      new Producer(s).start();
      new Consumer(s).start();
   }
}

class Shared
{
   private char c;

   private volatile boolean available;

   private final Lock lock;

   private final Condition condition;

   Shared()
   {
      available = false;
      lock = new ReentrantLock();
      condition = lock.newCondition();
   }

   Lock getLock()
   {
      return lock;
   }

   char getSharedChar()
   {
      lock.lock();
      try
      {
         while (!available)
            try
            {
               condition.await();
            }
            catch (InterruptedException ie)
            {
               ie.printStackTrace();
            }
         available = false;
         condition.signal();
      }
      finally
      {
         lock.unlock();
         return c;
      }
   }
   /**
    * P线程调用Shared 的 setSharedChar(Char c)方法去生成一个新的字符并且输入一个信息来验证生产出一个字符
    * 這个方法锁定前面创建的锁对象并且进入一个while循环,這个循环反复检测available可用性,当一个生产者生产的字符对消费者
    * 是可用的那么available为true. 当available是true的时候,P调用条件对象的condition.await()去等待available变为false
    * 当消费者消费一个字符的时候,signal这个条件对象去唤醒生产者. (使用循环代替if语句是因为虚假的唤醒是可能的并且available可能仍然为true)
    * 离开循环之后,P线程记录這个新字符,并且把available设为true去表示一个新的字符对于消费者来说是可用的,然后通知条件对象去唤醒一个等待的
    * C线程.最后释放锁并退出setSharedChar()方法.
    */
   void setSharedChar(char c)
   {
      lock.lock();
      try
      {
         while (available)
            try
            {
               condition.await();
            }
            catch (InterruptedException ie)
            {
               ie.printStackTrace();
            }
         this.c = c;
         available = true;
         condition.signal();
      }
      finally
      {
         lock.unlock();
      }
   }
}


class Producer extends Thread
{
   private final Lock l;

   private final Shared s;
   
   Producer(Shared s)
   {
      this.s = s;
      l = s.getLock();
   }

   /**
    * 注意: 這里锁定s.setSharedChar(ch)和输出,阻塞在P的run()方法中
    * s.getSharedChar()和输出阻塞在C线程的run()方法中
    * 以防止应用程序在产生消息之前输出消费消息，即使字符是在它们被消费之前产生的。
    */
   @Override
   public void run()
   {
      for (char ch = 'A'; ch <= 'Z'; ch++)
      {
         l.lock();
         s.setSharedChar(ch);
         System.out.println(ch + " produced by producer.");
         l.unlock();
      }
   }
}

class Consumer extends Thread
{
   private final Lock l;

   private final Shared s;

   Consumer(Shared s)
   {
      this.s = s;
      l = s.getLock();
   }

   @Override
   public void run()
   {
      char ch;
      do
      {
         l.lock();
         ch = s.getSharedChar();
         System.out.println(ch + " consumed by consumer.");
         l.unlock();
      }
      while (ch != 'Z');
   }
}